import Axios, {
  type AxiosInstance,
  type AxiosRequestConfig,
  type CustomParamsSerializer
} from 'axios'
import { stringify } from 'qs'
import type {
  RequestMethods,
  SimpleHttpError,
  SimpleHttpRequestConfig,
  SimpleHttpResponse
} from './types'
import { close, start } from '@/utils/nporgress'
import { whiteList } from '@/config'
import { message } from 'ant-design-vue'
import { useUserStoreHook } from '@/stores/modules/user'
import pageErrorEvent from '@/event/pageErrorEvent'
import {
  base64ToHex,
  decrypt,
  encryptByPublicKey,
  encryptToBase64,
  getAESKey
} from '@/utils/encrypt'

const defaultConfig: AxiosRequestConfig = {
  // 请求超时时间
  timeout: 60000,
  headers: {
    Accept: 'application/json, text/plain, */*',
    'Content-Type': 'application/json',
    'X-Requested-With': 'XMLHttpRequest'
  },
  paramsSerializer: {
    serialize: stringify as unknown as CustomParamsSerializer
  }
}

/**
 * 封装Http请求类
 */
class SimpleHttp {
  /** token过期后，暂存待执行的请求 */
  private static requests: Function[] = []
  /** 防止重复刷新token */
  private static isRefreshing = false
  /** 初始化配置对象 */
  private static initConfig: SimpleHttpRequestConfig = {}
  /** 保存当前Axios实例对象 */
  private static axiosInstance: AxiosInstance = Axios.create(defaultConfig)

  constructor() {
    this.httpInterceptorsRequest()
    this.httpInterceptorsResponse()
  }

  /**
   * 通用请求函数
   * @param method 请求方法
   * @param url 请求地址
   * @param encrypt 是否加密
   * @param param 请求参数
   * @param axiosConfig 其它配置
   * @param fileDownload 是否文件下载
   */
  public request<T>(
    method: RequestMethods,
    url: string,
    param?: AxiosRequestConfig,
    encrypt?: boolean,
    axiosConfig?: SimpleHttpRequestConfig,
    fileDownload?: boolean
  ): Promise<ResultData<T>> {
    const config = {
      method,
      url: import.meta.env.VITE_API_PREFIX + url,
      ...param,
      ...axiosConfig
    } as SimpleHttpRequestConfig
    let aesKey: string
    if (encrypt) {
      aesKey = getAESKey(16)
      config.data = {
        content: encryptToBase64(JSON.stringify(param?.data), aesKey),
        aesKey: base64ToHex(encryptByPublicKey(aesKey))
      }
    }
    // 单独处理自定义请求/响应回调
    return new Promise<ResultData<T>>((resolve, reject) => {
      SimpleHttp.axiosInstance.request(config).then((response: any) => {
        if (response == null) {
          reject('服务器异常')
          pageErrorEvent.emit('page:error:500', null)
          close()
          return
        }
        // 文件下载
        if (fileDownload) {
          resolve(response)
          return
        }
        if (response.code != 400) {
          // 解密
          if (encrypt) {
            response.data = JSON.parse(decrypt(response.data, aesKey))
          }
        }

        if (response.code >= 200 && response.code <= 300) {
          if (encrypt) {
            resolve({
              code: response.code,
              msg: response.msg,
              data: JSON.parse(response.data)
            } as ResultData<T>)
          } else {
            resolve(response as ResultData<T>)
          }
        } else if (response.code === 401) {
          // 状态码401 刷新token
          SimpleHttp.requests.push(() => {
            this.request<T>(method, url, param, encrypt, axiosConfig)
              .then((res: any) => {
                resolve(res)
              })
              .catch((err: any) => {
                reject(err)
              })
          })
          this.RefreshToken()
        } else if (response.code === 403) {
          pageErrorEvent.emit('page:error:403', null)
          reject(response.msg)
        } else if (response.code === 500) {
          pageErrorEvent.emit('page:error:500', null)
          reject(response.msg)
        } else if (response.status == 500) {
          pageErrorEvent.emit('page:error:500', null)
          reject('服务器异常')
        } else {
          reject(response.msg)
          message.error(response.msg)
        }
      })
    })
  }

  /** 请求拦截 */
  private httpInterceptorsRequest(): void {
    SimpleHttp.axiosInstance.interceptors.request.use(
      async (config: any): Promise<any> => {
        //打开进度条
        start()
        // 优先判断post/get等方法是否传入回调，否则执行初始化设置等回调
        if (typeof config.beforeRequestCallback === 'function') {
          config.beforeRequestCallback(config)
          return config
        }
        if (SimpleHttp.initConfig.beforeRequestCallback) {
          SimpleHttp.initConfig.beforeRequestCallback(config)
          return config
        }
        if (whiteList.find((url) => url === config.url)) return config
        config.headers['Authorization'] = useUserStoreHook().accessToken
        return config
      },
      (error) => {
        return Promise.reject(error)
      }
    )
  }

  /** 响应拦截 */
  private httpInterceptorsResponse(): void {
    SimpleHttp.axiosInstance.interceptors.response.use(
      (response: SimpleHttpResponse) => {
        //关闭进度条
        close()
        return response.data
      },
      (error: SimpleHttpError) => {}
    )
  }

  /**
   * 刷新token
   * @constructor
   * @private
   */
  private RefreshToken() {
    // 防止重复刷新
    if (SimpleHttp.isRefreshing) {
      return
    }
    // 未登录
    if (useUserStoreHook().refreshToken == null) {
      useUserStoreHook().logout()
      return
    }
    SimpleHttp.isRefreshing = true
    useUserStoreHook()
      .handRefreshToken()
      .then(() => {
        // 执行待刷新的请求
        SimpleHttp.requests.forEach((cb) => cb())
        // 清空待刷新的请求
        SimpleHttp.requests = []
      })
      .catch(() => {
        useUserStoreHook().logout()
      })
      .finally(() => {
        SimpleHttp.isRefreshing = false
      })
  }
}

export const http = new SimpleHttp()
